use std::net::{SocketAddr, ToSocketAddrs};

/*
pub(crate) trait CoolService {
    async fn heyhey(&self, x: usize) -> (String, usize);
    async fn yoyo(&self, u: String, v: [u32; 4]) -> Result<([String; 4], bool), i32>;
}
*/

// generated, both ends
#[derive(Debug, tweeny_rpc::serde::Serialize, tweeny_rpc::serde::Deserialize)]
pub(crate) enum CoolServiceReq {
    Call1(usize),
    Call2(String, [u32; 4]),
}
// generated, both ends
#[derive(Debug, tweeny_rpc::serde::Serialize, tweeny_rpc::serde::Deserialize)]
pub(crate) enum CoolServiceResp {
    Call1((String, usize)),
    Call2(Result<([String; 4], bool), i32>),
}

// generated, server end
pub(crate) trait CoolService <A> {
    fn heyhey(
        &self,
        from: A,
        x: usize,
    ) -> impl std::future::Future<Output = (String, usize)> + Send;
    fn yoyo(
        &self,
        from: A,
        u: String,
        v: [u32; 4],
    ) -> impl std::future::Future<Output = Result<([String; 4], bool), i32>> + Send;
}
// generated, server end
pub(crate) struct CoolServiceRpcServ<T>(pub(crate) T);
impl<A, T> tweeny_rpc::server::ServeRpc<A> for CoolServiceRpcServ<T>
where
    A: Send + 'static,
    T: CoolService<A> + Sync + Send + 'static,
{
    type Req = CoolServiceReq;
    type Resp = CoolServiceResp;

    fn dispatch_call(
        &self,
        req: Self::Req,
        from: A,
    ) -> impl futures::prelude::Future<Output = Self::Resp> + Send {
        async move {
            match req {
                CoolServiceReq::Call1(x) => CoolServiceResp::Call1(self.0.heyhey(from, x).await),
                CoolServiceReq::Call2(u, v) => CoolServiceResp::Call2(self.0.yoyo(from, u, v).await),
            }
        }
    }
}

// generated, client end
pub(crate) struct CoolServiceClient;
// generated, client end
impl CoolServiceClient {
    pub(crate) fn new<C, SD>(
        c: C,
        serder: SD,
    ) -> (
        tweeny_rpc::client::RequestingEnd<C, SD, CoolServiceReq, CoolServiceResp>,
        CoolServiceClientHandle,
    )
    where
        C: tokio::io::AsyncRead + tokio::io::AsyncWrite,
        SD: tweeny_rpc::courier::codec::ToWriterSerializer
            + tweeny_rpc::courier::codec::FromSliceDeserializer,
    {
        let (serving, handle) = tweeny_rpc::client::new_client(c, serder);
        (serving, CoolServiceClientHandle(handle))
    }
}
// generated, client end
#[derive(Clone)]
pub(crate) struct CoolServiceClientHandle(pub(crate) tweeny_rpc::client::RequestHandle<CoolServiceReq, CoolServiceResp>);
// generated, client end
impl CoolServiceClientHandle {
    pub(crate) async fn heyhey(
        &self,
        x: usize,
        timeout: Option<std::time::Duration>,
    ) -> Result<(String, usize), tweeny_rpc::client::error::CallError<CoolServiceReq>> {
        match self.0.call(CoolServiceReq::Call1(x), timeout).await? {
            CoolServiceResp::Call1(resp) => Ok(resp),
            _ => Err(tweeny_rpc::client::error::CallError::RespMismatch),
        }
    }
    pub(crate) async fn yoyo(
        &self,
        u: String,
        v: [u32; 4],
        timeout: Option<std::time::Duration>,
    ) -> Result<Result<([String; 4], bool), i32>, tweeny_rpc::client::error::CallError<CoolServiceReq>>
    {
        match self.0.call(CoolServiceReq::Call2(u, v), timeout).await? {
            CoolServiceResp::Call2(resp) => Ok(resp),
            _ => Err(tweeny_rpc::client::error::CallError::RespMismatch),
        }
    }
}

#[derive(Default)]
struct CoolServer {
    s: String,
}
impl<A: ToSocketAddrs> CoolService<A> for CoolServer {
    fn heyhey(
        &self,
        from: A,
        x: usize,
    ) -> impl std::future::Future<Output = (String, usize)> + Send {
        async { (String::new(), 0) }
    }
    fn yoyo(
        &self,
        from: A,
        u: String,
        v: [u32; 4],
    ) -> impl std::future::Future<Output = Result<([String; 4], bool), i32>> + Send {
        async {
            Ok((
                [String::new(), String::new(), String::new(), String::new()],
                true,
            ))
        }
    }
}

fn main() {
    use std::net::{IpAddr, Ipv4Addr};
    use std::sync::Arc;
    use tokio::net::TcpStream;
    use tweeny_rpc::{client::error::CallError, courier::codec::DefaultSerder, server::Server};

    let s = CoolServer::default();
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
    let server = Server::new_via_tcp(Arc::new(CoolServiceRpcServ(s)), addr);
    tokio::spawn(async move {
        match server.await {
            Ok(server) => {
                let _ = server.serve().await;
            },
            Err(e) => println!("serve error"),
        }
    });

    tokio::spawn(async move {
        let (serving, handle) =
            CoolServiceClient::new(TcpStream::connect(addr).await.unwrap(), DefaultSerder);
        tokio::spawn(serving);
        match handle.heyhey(12, None).await {
            Ok(ok) => println!("{ok:?}"),
            Err(e) => match e {
                CallError::Panic => todo!(),
                CallError::TimeOut => todo!(),
                CallError::RespLost => todo!(),
                CallError::RespMismatch => todo!(),
                CallError::LocalRequestServingTaskEnded(e) => todo!(),
            },
        }
    });
}
